package com.anc.redis.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;
import java.util.*;

/*********************************************************
 @author Anc
 @date 2021/7/01 15:13


 在service层需要使用缓存的增删查改方法上边加入注解
 @Cacheable 将方法的运行结果进行缓存；第二次再要相同的数据，直接从缓存中获取，不再调用方法；
 @CacheEvict 移除缓存
 @CachePut 修改了数据库的某个数据，同时更新缓存
 例子:
 @Cacheable(cacheNames= {"emp"}, unless="#result == null") //当结果为空时不缓存
 public Employee getEmp(Integer id) {
 return employeeMapper.getEmpById(id);
 }
 //key="#result.id" 或者 key="#employee.id" 都行
 @CachePut(cacheNames = {"emp"}, key = "#employee.id") //注意：修改缓存时，key要和放入的相同。getEmp方法缓存默认以id，该方法也应为id，否则不能成功更新缓存，而是以对象为id，将该方法的结果存入缓存。
 public Employee updateEmp(Employee employee) {
 System.out.println("updateEmp: " + employee);
 employeeMapper.updateEmp(employee);
 return employee;
 }
 //value和cacheNames一样的,#id可以取到参数里的id,#root.args[0]可以取到第一个参数
 @CacheEvict(value = "emp",key = "#id")
 public void deleteEmp(Integer id) {
 System.out.println("删除【" + id + "】");
 employeeMapper.deleteEmp(id);
 }
 参考文档:
 @link {https://blog.csdn.net/yangshengwei230612/article/details/108841645}
 *********************************************************/
@Configuration
@EnableCaching//开启注解式缓存
public class RedisCacheConfig {

    private final static int NO_PARAM_KEY = 0;

    /**
     * 生成key的策略 根据类名+方法名+所有参数的值生成唯一的一个key
     *
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            char sp = ':';
            StringBuilder sb = new StringBuilder(30);
            // 类名
            sb.append(target.getClass().getSimpleName());
            sb.append(sp);
            // 方法名
            sb.append(method.getName());
            sb.append(sp);
            if (params.length > 0) {
                for (int i = 0; i < params.length; i++) {
                    sb.append(Objects.toString(params[i]));
                    if (i < params.length - 1) {
                        sb.append(",");
                    }
                }
            } else {
                sb.append(NO_PARAM_KEY);
            }
            return sb.toString();
        };
    }

    @Bean
    public RedisCacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
        //这种注入的spring中的bean,需要的参数都是从容器中寻找已经注入的bean的,例如这个redisTemplate就是容器中已有bean,并且已经重写过序列化规则了
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        // 1.设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("redis:scm_logistics_company");

        // 2.对每个缓存空间应用不同的配置,为了不影响其他的缓存过期时间,这里只给这个缓存设置了过期时间
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("redis:scm_logistics_company", config.entryTtl(Duration.ofMinutes(1L)));

        return RedisCacheManager.builder(redisTemplate.getConnectionFactory())     // 使用自定义的缓存配置初始化一个cacheManager
                .initialCacheNames(cacheNames)  // 注意这两句的调用顺序，一定要先调用该方法设置初始化的缓存名，再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
    }
}